import logging
import os
from logging.handlers import RotatingFileHandler
from typing import Optional


class SingletonMeta(type):
    """Singleton metaclass to ensure only one instance of a class.

    作用说明：
    1. 元类是类的模板，通过重写__call__方法控制类的实例化过程，确保被修饰的类全局仅一个实例。
    2. 相比在类内部用__new__实现单例，元类的优势在于：
       - 可复用性：一次定义后可应用于多个类，减少代码冗余
       - 集中控制：单例逻辑统一维护，便于后续修改
       - 继承兼容性：子类无需额外处理即可继承单例特性
    """
    _instances = {}  # 存储类实例的缓存字典

    def __call__(cls, *args, **kwargs):
        """重写__call__方法以控制实例创建过程。

        Args:
            cls: 被实例化的类（例如 LoggingManager）
            *args: 类初始化时的位置参数
            **kwargs: 类初始化时的关键字参数

        Returns:
            类的唯一实例（若已创建则返回缓存实例）
        """
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class LoggingManager(metaclass=SingletonMeta):
    """基于单例模式的日志管理类。

    功能说明：
    - 全局唯一实例，确保日志配置一致性
    - 支持控制台和文件双输出，文件按大小自动轮转
    - 提供DEBUG/INFO/WARNING/ERROR/CRITICAL五级日志接口
    - 支持记录异常堆栈信息，便于问题排查
    """
    _initialized = False  # 标记日志配置是否已初始化

    def __init__(
        self,
        logger_name: str = "app_logger",
        log_file: Optional[str] = None,
        level: int = logging.INFO,
        max_bytes: int = 10 * 1024 * 1024,  # 10MB
        backup_count: int = 5,
        fmt_format: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        date_format: str = "%Y-%m-%d %H:%M:%S"
    ):
        """初始化日志配置（仅首次实例化时执行）。

        注意：由于单例模式限制，后续实例化调用不会重复执行配置逻辑。

        Args:
            logger_name: 日志器名称，用于区分不同日志实例（默认"app_logger"）
            log_file: 日志文件路径（为None时仅输出到控制台，默认None,支持使用 [./logs/app.log] 方式创建多层结构的日志文件）
            level: 日志级别（默认logging.INFO，低于该级别的日志不记录）
            max_bytes: 单个日志文件最大字节数（超过则轮转，默认10MB）
            backup_count: 日志轮转时保留的备份文件数量（默认5个）
            fmt_format: 日志格式字符串（默认包含时间、名称、级别、消息）
            date_format: 日志中时间的显示格式（默认"年-月-日 时:分:秒"）
        """
        if not self._initialized:
            self.logger = logging.getLogger(logger_name)
            self.logger.setLevel(level)
            self.logger.handlers.clear()  # 清除默认处理器，避免重复输出

            # 定义日志格式
            formatter = logging.Formatter(fmt=fmt_format, datefmt=date_format)

            # 添加控制台处理器
            console_handler = logging.StreamHandler()
            console_handler.setFormatter(formatter)
            self.logger.addHandler(console_handler)

            # 添加文件处理器（若指定路径）
            if log_file:
                log_dir = os.path.dirname(log_file)
                if log_dir and not os.path.exists(log_dir):
                    os.makedirs(log_dir, exist_ok=True)

                # 轮转文件处理器
                file_handler = RotatingFileHandler(
                    filename=log_file,
                    maxBytes=max_bytes,
                    backupCount=backup_count,
                    encoding="utf-8"
                )
                file_handler.setFormatter(formatter)
                self.logger.addHandler(file_handler)

            self._initialized = True

    @classmethod
    def debug(cls, message: str) -> None:
        """输出DEBUG级别日志（详细的调试信息）。

        Args:
            message: 日志内容字符串
        """
        cls().logger.debug(message)

    @classmethod
    def info(cls, message: str) -> None:
        """输出INFO级别日志（程序正常运行的信息）。

        Args:
            message: 日志内容字符串
        """
        cls().logger.info(message)

    @classmethod
    def warning(cls, message: str) -> None:
        """输出WARNING级别日志（异常但不致命的问题）。

        Args:
            message: 日志内容字符串
        """
        cls().logger.warning(message)

    @classmethod
    def error(cls, message: str, exc_info: bool = False) -> None:
        """输出ERROR级别日志（影响部分功能的错误）。

        Args:
            message: 日志内容字符串
            exc_info: 是否记录异常堆栈（默认False，异常时建议设为True）
        """
        cls().logger.error(message, exc_info=exc_info)

    @classmethod
    def critical(cls, message: str, exc_info: bool = False) -> None:
        """输出CRITICAL级别日志（导致程序终止的严重错误）。

        Args:
            message: 日志内容字符串
            exc_info: 是否记录异常堆栈（默认False，严重错误时建议设为True）
        """
        cls().logger.critical(message, exc_info=exc_info)

    @classmethod
    def get_logger(cls) -> logging.Logger:
        """获取原生日志器实例，用于高级配置。

        Returns:
            logging.Logger: 原生日志器对象
        """
        return cls().logger